package com.yugao.fintech.antelope.infra.weixin.mp.handler;

import cn.hutool.core.util.URLUtil;
import com.binarywang.spring.starter.wxjava.mp.enums.HttpClientType;
import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
import com.yugao.fintech.antelope.base.config.core.ConfigFactory;
import com.yugao.fintech.antelope.infra.common.QrCodeSceneEnum;
import com.yugao.fintech.antelope.infra.common.ScanScene;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.scanbiz.BaseWxScanEventHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.menu.MenuHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.message.MessageAutoReplyHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.message.MessageReceiveHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.other.KfSessionHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.other.NullHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.other.ScanHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.other.StoreCheckNotifyHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.user.LocationHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.user.SubscribeHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.handler.wx.user.UnsubscribeHandler;
import com.yugao.fintech.antelope.infra.weixin.mp.model.WxMpContext;
import com.yugao.fintech.antelope.base.model.enums.ConfigDefineEnum;
import com.yugao.fintech.antelope.base.model.enums.ConfigKeyEnum;
import com.yugao.fintech.antelope.base.model.module.config.ConfigInfo;
import com.yugao.fintech.antelope.base.model.module.config.param.BaseConfigParam;
import com.yugao.fintech.antelope.base.model.module.config.param.WxMpParam;
import com.yugao.fintech.antelope.base.model.module.tenant.TenantContextHolder;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.core.utils.SpringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpQrcodeService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.api.impl.WxMpServiceJoddHttpImpl;
import me.chanjar.weixin.mp.api.impl.WxMpServiceOkHttpImpl;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@Service
@RequiredArgsConstructor
public class WxMpHandler {
    /**
     * key: 租户id
     */
    private static final Map<Long, WxMpContext> mpContextCache = new ConcurrentHashMap<>();

    private final MessageReceiveHandler messageReceiveHandler;
    private final KfSessionHandler kfSessionHandler;
    private final StoreCheckNotifyHandler storeCheckNotifyHandler;
    private final MenuHandler menuHandler;
    private final NullHandler nullHandler;
    private final SubscribeHandler subscribeHandler;
    private final UnsubscribeHandler unsubscribeHandler;
    private final LocationHandler locationHandler;
    private final ScanHandler scanHandler;
    private final MessageAutoReplyHandler messageAutoReplyHandler;
    private final ConfigFactory configFactory;

    /**
     * 获取上下文
     */
    public WxMpContext getContext() {
        Long tenantId = TenantContextHolder.get().getTenantId();
        WxMpContext wxMpContext = mpContextCache.get(tenantId);
        if (Objects.isNull(wxMpContext)) {
            ConfigInfo config = configFactory.getConfigOfNonNull(ConfigDefineEnum.WX_MP);
            WxMpParam mpParam = BaseConfigParam.jsonToObject(ConfigKeyEnum.ofAndCheck(config.getConfigKey()), config.getConfigValue());
            wxMpContext = getContextAndAutoCreate(mpParam);
            if (Objects.isNull(wxMpContext)) {
                throw new BizException("尚未配置微信相关参数, 请到控制台进行配置");
            }
        }
        return wxMpContext;
    }

    /**
     * 通过微信账号id获取上下文
     * @param param 微信参数
     */
    public WxMpContext getContextAndAutoCreate(WxMpParam param) {
        Long tenantId = TenantContextHolder.get().getTenantId();
        WxMpContext wxMpContext = mpContextCache.get(tenantId);
        if (Objects.isNull(wxMpContext)) {
            synchronized (String.valueOf(tenantId).intern()) {
                wxMpContext = createContext(param);
                mpContextCache.put(tenantId, wxMpContext);
            }
        }
        return wxMpContext;
    }

    private WxMpContext createContext(WxMpParam param) {
        try {
            WxMpContext context = new WxMpContext();
            // 这里读取自动配置的值
            WxMpProperties properties = SpringUtils.getBean(WxMpProperties.class);
            WxMpProperties.ConfigStorage configStorage = properties.getConfigStorage();

            // 初始哈存储配置
            WxMpProperties clientProperties = context.getProperties();
            WxMpProperties.ConfigStorage currentConfigStorage = clientProperties.getConfigStorage();
            currentConfigStorage.setType(configStorage.getType());
            currentConfigStorage.setHttpClientType(configStorage.getHttpClientType());
            currentConfigStorage.setHttpProxyHost(configStorage.getHttpProxyHost());
            currentConfigStorage.setHttpProxyPassword(configStorage.getHttpProxyPassword());
            currentConfigStorage.setKeyPrefix(configStorage.getKeyPrefix());

            // 覆盖token
            clientProperties.setAppId(param.getAppId());
            clientProperties.setToken(param.getToken());
            clientProperties.setAesKey(param.getEncodingAesKey());
            clientProperties.setSecret(param.getAppSecret());

            StringRedisTemplate redisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
            WxMpRedisConfigImpl wxMpConfigStorage = new WxMpRedisConfigImpl(new RedisTemplateWxRedisOps(redisTemplate), WxMpContext.KEY_PRE);
            wxMpConfigStorage.setAppId(clientProperties.getAppId());
            wxMpConfigStorage.setAesKey(clientProperties.getAesKey());
            wxMpConfigStorage.setSecret(clientProperties.getSecret());
            wxMpConfigStorage.setToken(clientProperties.getToken());

            HttpClientType httpClientType = clientProperties.getConfigStorage().getHttpClientType();
            me.chanjar.weixin.mp.api.WxMpService service;
            switch (httpClientType) {
                case OkHttp:
                    service = new WxMpServiceOkHttpImpl();
                    break;
                case JoddHttp:
                    service = new WxMpServiceJoddHttpImpl();
                    break;
                case HttpClient:
                    service = new WxMpServiceHttpClientImpl();
                    break;
                default:
                    service = new WxMpServiceImpl();
            }
            service.setWxMpConfigStorage(wxMpConfigStorage);
            context.setService(service);
            context.setMpParam(param);

            WxMpMessageRouter wxMpMessageRouter = buildMpMessageRouter(service);
            context.setRouter(wxMpMessageRouter);
            return context;
        } catch (WxRuntimeException e) {
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 删除缓存
     */
    public void deleteCache() {
        mpContextCache.remove(TenantContextHolder.get().getTenantId());
    }

    /**
     * 获取永久二维码图片地址
     */
    public String getQrcode(ScanScene req) {
        if (Objects.isNull(req)) {
            throw new BizException("扫码场景不能为空");
        }
        try {
            // 获取永久二维码的ticket，其中scene为场景值
            QrCodeSceneEnum scene = Objects.isNull(req.getSceneCode()) ?
                    QrCodeSceneEnum.DEFAULT : QrCodeSceneEnum.of(req.getSceneCode());

            if (Boolean.TRUE.equals(req.getIsRefresh())) {
                this.deleteCache();
            }

            WxMpQrcodeService qrcodeService = this.getContext().getService().getQrcodeService();
            BaseWxScanEventHandler eventHandler = BaseWxScanEventHandler.getInstance(scene);
            String qrCodeTicket = eventHandler.getQrCodeTicket(req);
            WxMpQrCodeTicket ticket;
            if (Boolean.TRUE.equals(req.getIsTmpTicket())) {
                ticket = qrcodeService.qrCodeCreateTmpTicket(qrCodeTicket, req.getExpireSeconds());
            } else {
                ticket = qrcodeService.qrCodeCreateLastTicket(qrCodeTicket);
            }
            return  "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + URLUtil.encode(ticket.getTicket());
        } catch (WxErrorException e) {
            log.error("error: {}", e.getMessage());
            throw new BizException(e.getError().getErrorMsg());
        }
    }

    private WxMpMessageRouter buildMpMessageRouter(me.chanjar.weixin.mp.api.WxMpService mpService) {
        WxMpMessageRouter router = new WxMpMessageRouter(mpService);
        // 记录所有事件的日志（异步执行）
        router.rule().handler(messageReceiveHandler).next();

        // 接收客服会话管理事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
                .handler(kfSessionHandler).end();
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
                .handler(kfSessionHandler)
                .end();
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
                .handler(kfSessionHandler).end();

        // 门店审核事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxMpEventConstants.POI_CHECK_NOTIFY)
                .handler(storeCheckNotifyHandler).end();

        // 自定义菜单事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end();

        // 点击菜单连接事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end();

        // 关注事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler)
                .end();

        // 取消关注事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.UNSUBSCRIBE)
                .handler(unsubscribeHandler).end();

        // 上报地理位置事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.LOCATION).handler(locationHandler)
                .end();

        // 接收地理位置消息
        router.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
                .handler(locationHandler).end();

        // 扫码事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SCAN).handler(scanHandler).end();

        // 默认
        router.rule().async(false).handler(messageAutoReplyHandler).end();
        return router;
    }
}
